package game;

import hud.StatusScreen;

import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import jmetest.terrain.TestIsland;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.ChaseCamera;
import com.jme.input.ThirdPersonHandler;
import com.jme.math.FastMath;
import com.jme.math.Plane;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.Spatial.TextureCombineMode;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jmex.effects.water.ProjectedGrid;
import com.jmex.effects.water.WaterHeightGenerator;
import com.jmex.effects.water.WaterRenderPass;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.RawHeightMap;

public class GameStart extends GameBase {

	protected TerrainPage terrain;
	protected Skybox skybox;
	// For Water
	protected Node reflectedNode;
	protected WaterRenderPass waterEffectRenderPass;
	protected ProjectedGrid projectedGrid;
	protected float farPlane = 10000.0f;
	protected ChaseCamera chaser;
	protected Node player;
	protected StatusScreen statusNode;

	public static void main(String[] args) {
		Logger.getLogger("").setLevel(Level.WARNING);
		GameStart game = new GameStart();
		game.setConfigShowMode(ConfigShowMode.ShowIfNoConfig);
		game.start();
	}

	@Override
	protected void simpleUpdate() {
		/** Test **/
		statusNode.update(0.25f, 0.5f, 0.75f, 1f);		
		
		chaser.update(tpf);
		
		cam.update();

		float camMinHeight = terrain.getHeight(cam.getLocation()) + 2f;
		if (!Float.isInfinite(camMinHeight) && !Float.isNaN(camMinHeight)
				&& cam.getLocation().y <= camMinHeight) {
			cam.getLocation().y = camMinHeight;
			cam.update();
		}
		if (cam.getLocation().y <= 30) {
			cam.getLocation().y = 30;
			cam.update();
		}
		skybox.setLocalTranslation(cam.getLocation());
		float characterMinHeight = terrain.getHeight(player
				.getLocalTranslation())
				+ ((BoundingBox) player.getWorldBound()).yExtent;
		if (!Float.isInfinite(characterMinHeight)
				&& !Float.isNaN(characterMinHeight)) {
			player.getLocalTranslation().y = characterMinHeight;
		}
	}
	
	@Override
	protected void simpleInitGame() {
		cam.setFrustumPerspective(50.0f, (float) display.getWidth()
				/ (float) display.getHeight(), 1f, farPlane);
		cam.setLocation(new Vector3f(100, 50, 100));
		cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
		cam.update();

		setupFog();
		reflectedNode = new Node("reflectNode");
		setupSkybox();
		reflectedNode.attachChild(skybox);
		scene.attachChild(reflectedNode);
		setupWater();
		setupPlayer();
		setupHandler();
		setupTerrain();
		reflectedNode.attachChild(terrain);
		setupHud();
	}
	
	protected void setupHud() {
		statusNode = new StatusScreen();
		hud.attachChild(statusNode);
		statusNode.setup("Player1");
	}

	protected void setupPlayer() {
		Box b = new Box("box", new Vector3f(), 5, 5, 5);
		b.setModelBound(new BoundingBox());
		b.updateModelBound();
		player = new Node("Player");
		reflectedNode.attachChild(player);
		player.attachChild(b);
		player.updateWorldBound(); // We do this to allow the camera setup
		// access to the world bound in our setup
		// code.

		TextureState ts = display.getRenderer().createTextureState();
		ts.setEnabled(true);
		ts.setTexture(TextureManager.loadTexture(getClass().getClassLoader()
				.getResource("jmetest/data/images/Monkey.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear));
		player.setRenderState(ts);
	}

	protected void setupHandler() {
		// Setup chase camera
		Vector3f targetOffset = new Vector3f();
		targetOffset.y = ((BoundingBox) player.getWorldBound()).yExtent * 1.5f;
		chaser = new ChaseCamera(cam, player);
		chaser.setTargetOffset(targetOffset);
		// Setup input handler
		HashMap<String, Object> handlerProps = new HashMap<String, Object>();
		handlerProps.put(ThirdPersonHandler.PROP_DOGRADUAL, "true");
		handlerProps.put(ThirdPersonHandler.PROP_TURNSPEED, ""
				+ (1.0f * FastMath.PI));
		handlerProps.put(ThirdPersonHandler.PROP_LOCKBACKWARDS, "false");
		handlerProps.put(ThirdPersonHandler.PROP_CAMERAALIGNEDMOVE, "true");
		input = new ThirdPersonHandler(player, cam, handlerProps);
		input.setActionSpeed(100f);
	}

	protected void setupFog() {
		FogState fogState = display.getRenderer().createFogState();
		fogState.setDensity(1.0f);
		fogState.setEnabled(true);
		fogState.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
		fogState.setEnd(farPlane);
		fogState.setStart(farPlane / 10.0f);
		fogState.setDensityFunction(FogState.DensityFunction.Linear);
		fogState.setQuality(FogState.Quality.PerVertex);
		scene.setRenderState(fogState);
	}

	protected void setupSkybox() {
		skybox = new Skybox("skybox", 10, 10, 10);

		String dir = "jmetest/data/skybox1/";
		Texture north = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "1.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture south = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "3.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture east = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "2.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture west = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "4.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture up = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "6.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture down = TextureManager.loadTexture(getClass().getClassLoader()
				.getResource(dir + "5.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);

		skybox.setTexture(Skybox.Face.North, north);
		skybox.setTexture(Skybox.Face.West, west);
		skybox.setTexture(Skybox.Face.South, south);
		skybox.setTexture(Skybox.Face.East, east);
		skybox.setTexture(Skybox.Face.Up, up);
		skybox.setTexture(Skybox.Face.Down, down);
		skybox.preloadTextures();

		CullState cullState = display.getRenderer().createCullState();
		cullState.setCullFace(CullState.Face.None);
		cullState.setEnabled(true);
		skybox.setRenderState(cullState);

		ZBufferState zState = display.getRenderer().createZBufferState();
		zState.setEnabled(false);
		skybox.setRenderState(zState);

		FogState fs = display.getRenderer().createFogState();
		fs.setEnabled(false);
		skybox.setRenderState(fs);

		skybox.setLightCombineMode(Spatial.LightCombineMode.Off);
		skybox.setCullHint(Spatial.CullHint.Never);
		skybox.setTextureCombineMode(TextureCombineMode.Replace);
		skybox.updateRenderState();

		skybox.lockBounds();
		skybox.lockMeshes();
	}

	protected void setupWater() {

		waterEffectRenderPass = new WaterRenderPass(cam, 4, true, true);
		waterEffectRenderPass.setClipBias(0.5f);
		waterEffectRenderPass.setWaterMaxAmplitude(5.0f);
		// setting to default value just to show
		waterEffectRenderPass.setWaterPlane(new Plane(new Vector3f(0.0f, 1.0f,
				0.0f), 0.0f));

		projectedGrid = new ProjectedGrid("ProjectedGrid", cam, 100, 70, 0.01f,
				new WaterHeightGenerator());

		waterEffectRenderPass.setWaterEffectOnSpatial(projectedGrid);
		scene.attachChild(projectedGrid);
		projectedGrid.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

		waterEffectRenderPass.setReflectedScene(reflectedNode);
		waterEffectRenderPass.setSkybox(skybox);
		pManager.add(waterEffectRenderPass);

		RenderPass rootPass = new RenderPass();
		rootPass.add(scene);
		pManager.add(rootPass);
	}

	protected void setupTerrain() {
		RawHeightMap heightMap = new RawHeightMap(TestIsland.class
				.getClassLoader().getResource(
						"jmetest/data/texture/terrain/heights.raw"), 129,
				RawHeightMap.FORMAT_16BITLE, false);

		Vector3f terrainScale = new Vector3f(2, 0.001f, 2);
		heightMap.setHeightScale(0.001f);
		terrain = new TerrainPage("Terrain", 33, heightMap.getSize(),
				terrainScale, heightMap.getHeightMap());
		terrain.getLocalTranslation().set(0, -9.5f, 0);
		terrain.setDetailTexture(1, 1);

		scene.attachChild(terrain);

	}

	protected void cleanup() {
		super.cleanup();
		waterEffectRenderPass.cleanup();
	}
}
